package gsw.tool.eventbus.eventbus;

import android.os.Handler;
import android.os.Looper;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 事件总线框架
 */
public class EventBus implements IEventBus {
    private static EventBus instance = new EventBus();
    //线程池
    private ExecutorService executorService;
    //总表,保存注册的类与方法
    private Map<Object, List<SubscribleMethod>> cacheMap;
    //主线程的Hanlder，用于在子线程执行执行主线程的Runnable
    private Handler handler;

    public static EventBus getInstance() {
        return instance;
    }

    private EventBus() {
        this.cacheMap = new HashMap<>();
        executorService = Executors.newCachedThreadPool();
        handler = new Handler(Looper.getMainLooper());
    }

    /**
     * 注册类型与方法
     *
     * @param activity 注册的类，一般是Activity、Fragment等对象
     */
    public void register(Object activity) {
        List<SubscribleMethod> list = cacheMap.get(activity);
        //如果已经注册  就不需要注册
        if (list == null) {
            list = getSubscribeMethods(activity);
            cacheMap.put(activity, list);
        }
    }

    /**
     * 查找注册的方法
     *
     * @param activity 注册的类
     */
    private List<SubscribleMethod> getSubscribeMethods(Object activity) {
        List<SubscribleMethod> list = new ArrayList<>();
        Class clazz = activity.getClass();
        //遍历父类寻找
        while (clazz != null) {
            String name = clazz.getName();
            //如果是系统的类，就跳过
            if (name.startsWith("java.") || name.startsWith("javax.") || name.startsWith("android.")) {
                break;
            }
            Method[] methods = clazz.getDeclaredMethods();
            //遍历Method，寻找带Subscribe注解，并且只有一个参数的Method
            for (Method method : methods) {
                Subscribe subscribe = method.getAnnotation(Subscribe.class);
                if (subscribe == null) {
                    continue;
                }
                Class[] paratems = method.getParameterTypes();
                //只支持一个参数
                if (paratems.length != 1) {
                    throw new RuntimeException("eventbus只能接收到一个参数");
                }
                ThreadMode threadMode = subscribe.threadMode();
                SubscribleMethod subscribleMethod = new SubscribleMethod(method, threadMode, paratems[0]);
                list.add(subscribleMethod);
            }
            //从父类查找
            clazz = clazz.getSuperclass();
        }

        return list;
    }

    /**
     * 发消息，并且自动处理
     *
     * @param msg 发送的消息。只支持一个参数
     */
    public void post(final Object msg) {
        Set<Object> set = cacheMap.keySet();
        Iterator iterator = set.iterator();
        //遍历cacheMap，查找参数类型与msg相同的注册方法
        while (iterator.hasNext()) {
            //注册的类
            final Object activity = iterator.next();
            //注册的方法
            List<SubscribleMethod> list = cacheMap.get(activity);
            for (final SubscribleMethod subscribleMethod : list) {
                //判断参数类型与msg是否相同
                if (subscribleMethod.getEventType().isAssignableFrom(msg.getClass())) {
                    switch (subscribleMethod.getThreadMode()) {
                        //订阅者将在发布事件的同一线程中调用。这是默认值。
                        case PostThread:
                            invoke(subscribleMethod, activity, msg);
                            break;
                        //订阅者将在Android的主线程（有时称为UI线程）中调用。
                        case MainThread:
                            //如果当前是主线程，则直接执行
                            if (Looper.myLooper() == Looper.getMainLooper()) {
                                invoke(subscribleMethod, activity, msg);
                            } else {//如果当前是子线程，则使用handler在主线程执行
                                handler.post(new Runnable() {
                                    @Override
                                    public void run() {
                                        invoke(subscribleMethod, activity, msg);
                                    }
                                });
                            }
                            break;
                        //订阅者将在后台线程中调用。
                        case BackgroundThread:
                            //如果当前是主线程，则新开线程执行
                            if (Looper.myLooper() == Looper.getMainLooper()) {
                                executorService.execute(new Runnable() {
                                    @Override
                                    public void run() {
                                        invoke(subscribleMethod, activity, msg);
                                    }
                                });
                            } else {//如果当前是子线程，则直接执行
                                invoke(subscribleMethod, activity, msg);
                            }
                            break;
                        //事件处理程序方法在单独的线程中调用。这始终独立于发布线程和主线程。
                        case Async:
                            executorService.execute(new Runnable() {
                                @Override
                                public void run() {
                                    invoke(subscribleMethod, activity, msg);
                                }
                            });
                            break;
                    }
                }
            }
        }
    }

    /**
     * 反射调用Method
     *
     * @param subscribleMethod 注册的方法
     * @param activity         注册的类型
     * @param msg              发送的消息
     */
    private void invoke(SubscribleMethod subscribleMethod, Object activity, Object msg) {
        Method method = subscribleMethod.getMethod();
        try {
            method.invoke(activity, msg);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }

    /**
     * 反注册类型与方法
     *
     * @param activity 注册的类，一般是Activity、Fragment等对象
     */
    public void unRegister(Object activity) {
        cacheMap.remove(activity);
    }
}
